/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. 
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include "fs.h"
#include "irq.h"
#include "test.h"
#include "time.h"
#include "stdio.h"
#include "sched.h"
#include "screen.h"
#include "common.h"
#include "syscall.h"
#include "smp.h"
#include "mm.h"
#include "mac.h"

#define TASK_INIT (00)

queue_t ready_queue;
queue_t block_queue;
queue_t sleeping_queue;

uint64_t STACK_TOP = STACK_MIN;
uint64_t exception_handlers[32];

static void init_memory()
{
}
static void init_pcb()
{
    int i, j, k;

    for (i = 0; i < NUM_MAX_TASK; i++)
    {
        pcb[i].prev = NULL;
        pcb[i].next = NULL;
        bzero(&pcb[i], sizeof(pcb[i]));
        pcb[i].pid = i;
    }
    strcpy(pcb[0].name, "init");
    pcb[0].status = TASK_EXITED;
    pcb[0].type = USER_PROCESS;
    pcb[0].kernel_state = FALSE;
    pcb[0].kernel_context.reg[29] = STACK_TOP;
    pcb[0].user_context.reg[29] = STACK_TOP + STACK_SIZE;

    queue_init(&ready_queue);
    queue_init(&block_queue);
    queue_init(&sleeping_queue);

    // init Lock array
    for (i = 0; i < LOCK_TOTAL_NUM; i++)
    {
        queue_init(&Lock[i].mutex_lock_queue);
        Lock[i].status = UNLOCKED;
        Lock[i].lock_id = i;
    }

    // fill pcb array
    for (j = 0; j < num_sched2_tasks; j++)
    {
        strcpy(pcb[j + 1].name, sched2_tasks[j]->name);
        pcb[j + 1].entry_point = sched2_tasks[j]->entry_point;
        pcb[j + 1].type = sched2_tasks[j]->type;
        pcb[j + 1].status = TASK_READY;
        pcb[j + 1].kernel_state = 0;
        pcb[j + 1].priority = sched2_tasks[j]->priority;
        pcb[j + 1].init_priority = sched2_tasks[j]->priority;

        // give kernel_context an initial entry, change to user status
        pcb[j + 1].kernel_context.reg[31] = (uint64_t)first_entry;

        pcb[j + 1].kernel_context.cp0_status = 0x10008001;
        pcb[j + 1].user_context.cp0_status = 0x10008001;

        pcb[j + 1].kernel_context.reg[29] = STACK_TOP + (2 * j) * STACK_SIZE;
        pcb[j + 1].user_context.reg[29] = STACK_TOP + (2 * j + 1) * STACK_SIZE;

        pcb[j + 1].user_context.cp0_epc = sched2_tasks[j]->entry_point;
        pcb[j + 1].kernel_context.cp0_epc = sched2_tasks[j]->entry_point;
        // init lock[]
        for (k = 0; k < LOCK_MAX_NUM; k++)
        {
            pcb[j + 1].lock[k] = 0;
        }
        pcb[j + 1].lock_num = 0;
        // push pcb into ready_queue
        queue_push(&ready_queue, &pcb[j + 1]);
    }

    // initial current_running pointer
    current_running = &pcb[0];
}

static void init_exception_handler()
{
    int i = 0;
    for (i = 0; i < 32; i++)
    {
        exception_handlers[i] = (uint64_t)&handle_other;
    }
    exception_handlers[INT] = (uint64_t)&handle_int;
    exception_handlers[SYS] = (uint64_t)&handle_syscall;
}

static void init_exception()
{
    /* init cp0_status */
    /* close interrupt */
    uint64_t cp0_status = get_cp0_status();
    cp0_status = cp0_status & 0xfffffffe;
    set_cp0_status(cp0_status);

    /* set pointer array */
    init_exception_handler();

    /* move function to 0x80000180 */
    memcpy((void *)(BEV0_EBASE + BEV0_OFFSET), (void *)exception_handler_begin, exception_handler_end - exception_handler_begin);

    memcpy((void *)(BEV1_EBASE + BEV1_OFFSET), (void *)exception_handler_begin, exception_handler_end - exception_handler_begin);

    /* set COUNT & set COMPARE */
    reset_timer();

}

// [2]
// extern int read_shell_buff(char *buff);

static void init_syscall(void)
{
    int fn;
    // init all the pointer in syscall array
    for (fn = 0; fn < NUM_SYSCALLS; fn++)
    {
        syscall[fn] = (uint64_t(*)()) & invalid_syscall;
    }

    syscall[SYSCALL_CURSOR] = (uint64_t(*)()) & screen_move_cursor;
    syscall[SYSCALL_MUTEX_LOCK_INIT] = (uint64_t(*)()) & do_mutex_lock_init;
    syscall[SYSCALL_MUTEX_LOCK_ACQUIRE] = (uint64_t(*)()) & do_mutex_lock_acquire;
    syscall[SYSCALL_MUTEX_LOCK_RELEASE] = (uint64_t(*)()) & do_mutex_lock_release;
    syscall[SYSCALL_SLEEP] = (uint64_t(*)()) & do_sleep;
    syscall[SYSCALL_GET_TIMER] = (uint64_t(*)()) & get_timer;
    syscall[SYSCALL_DO_SCHEDULER] = (uint64_t(*)()) & do_scheduler;
    syscall[SYSCALL_WRITE] = (uint64_t(*)()) & do_write;
    syscall[SYSCALL_BINSEMOP] = (uint64_t(*)()) & do_binsemop;
    syscall[SYSCALL_BINSEMGET] = (uint64_t(*)()) & do_binsemget;
}

/* [0] The beginning of everything >_< */
void __attribute__((section(".entry_function"))) _start(void)
{
    char *msg = "\rHello OS!\n\r";
    void (*pprintstr)(char *str);
    pprintstr = (void *)0xffffffff8f0d5534;
    (*pprintstr)(msg);
    asm_start();

    /* init stack space */
    //init_stack();
    //printk("\r> [INIT] Stack heap initialization succeeded.\n");

    /* init interrupt */
    init_exception();
    printk("\r> [INIT] Interrupt processing initialization succeeded.\n");

    //init_memory();
    //printk("\r> [INIT] Virtual memory initialization succeeded.\n");
    // init system call table (0_0)
    /* init system call table */

    init_syscall();
    printk("\r> [INIT] System call initialized successfully.\n");

    /* init Process Control Block */

    init_pcb();
    printk("\r> [INIT] PCB initialization succeeded.\n");

    /* init screen */
    init_screen();
    //printk("\r> [INIT] SCREEN initialization succeeded.\n");

    /* init filesystem */
    //read_super_block();

    /* wake up core1*/
    //loongson3_boot_secondary();

    /* set cp0_status register to allow interrupt */
    // enable exception and interrupt
    // ERL = 0, EXL = 0, IE = 1
    reset_timer();
    set_cp0_status(0x10008001);
    uint64_t count, compare;
    while (1)
    {
    };
    return;
}
